import { defineStore } from 'pinia';
import { sm2, sm3, sm4 } from 'sm-crypto-v2';
import CryptoJS from 'crypto-js';
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min';
import hmacSHA512 from 'crypto-js/hmac-sha512';
import { v4 as uuidv4 } from 'uuid';
import { getCryptConf, negotiateCrypt } from '@/api/common';

const from = import.meta.env.VITE_HMAC_FROM;

const cryptStore = defineStore('crypt', {
  state: () => ({
    cryptConf: {},
    cryptKeys: {},
    cryptInitialized: false,
    negotiating: false,
    xSessionId: '',
  }),
  getters: {
    isCryptInitialized() {
      return this.cryptInitialized;
    },
    isNegotiating() {
      return this.negotiating;
    },
  },
  actions: {
    genSymmetricKey() {
      let symmetricKey;
      if ('SM4' === this.cryptConf.symmetricType) {
        let salt = CryptoJS.lib.WordArray.random(128 / 8);
        let key128Bits1000Iterations = CryptoJS.PBKDF2('Secret Passphrase', salt, {
          keySize: 128 / 32,
          iterations: 1000,
        });
        symmetricKey = key128Bits1000Iterations.toString(CryptoJS.enc.Hex);
      } else if ('AES' === this.cryptConf.symmetricType) {
        var salt = CryptoJS.lib.WordArray.random(128 / 8);
        var key256Bits1000Iterations = CryptoJS.PBKDF2('Secret Passphrase', salt, {
          keySize: 256 / 32,
          iterations: 1000,
        });
        symmetricKey = key256Bits1000Iterations.toString(CryptoJS.enc.Hex);
      } else {
        throw '与后端协商秘钥失败，不支持的对称加密算法类型：' + this.cryptConf.symmetricType;
      }
      return symmetricKey;
    },
    genHmacKey() {
      let hmacKey;
      if ('HmacSM3' === this.cryptConf.hmacType || 'HmacSHA512' === this.cryptConf.hmacType) {
        let salt = CryptoJS.lib.WordArray.random(128 / 8);
        let key128Bits1000Iterations = CryptoJS.PBKDF2('Secret Passphrase', salt, {
          keySize: 128 / 32,
          iterations: 1000,
        });
        hmacKey = key128Bits1000Iterations.toString(CryptoJS.enc.Hex);
      } else {
        throw '与后端协商秘钥失败，不支持的hmac算法类型：' + this.cryptConf.symmetricType;
      }
      return hmacKey;
    },
    genEncryptedKeys(symmetricKey, hmacKey) {
      let encryptedKeys;
      if ('SM2' === this.cryptConf.asymmetricType) {
        let encryptedSymmetricKey = sm2.doEncrypt(symmetricKey, this.cryptConf.hexPublicKey, 1);
        let encryptedHmacKey = sm2.doEncrypt(hmacKey, this.cryptConf.hexPublicKey, 1);
        encryptedKeys = {
          encryptedSymmetricKey: encryptedSymmetricKey,
          encryptedHmacKey: encryptedHmacKey,
        };
      } else if ('RSA' === this.cryptConf.asymmetricType) {
        let encryptedSymmetricKey = this.rsaEncryptByKey(symmetricKey, this.cryptConf.hexPublicKey);
        let encryptedHmacKey = this.rsaEncryptByKey(hmacKey, this.cryptConf.hexPublicKey);
        encryptedKeys = {
          encryptedSymmetricKey: encryptedSymmetricKey,
          encryptedHmacKey: encryptedHmacKey,
        };
      } else {
        throw '与后端协商秘钥失败，不支持的非对称加密算法类型：' + this.cryptConf.symmetricType;
      }
      return encryptedKeys;
    },
    negotiateKey() {
      if (this.negotiating) {
        return new Promise((resolve) => setTimeout(resolve, 100));
      }

      this.negotiating = true;
      return getCryptConf()
        .then((res) => {
          this.cryptConf = res.data;
          if (this.cryptConf && this.cryptConf.requestCryptEnable) {
            // 按对称加密算法策略，生成对称加密秘钥
            const symmetricKey = this.genSymmetricKey();
            // 按接口校验策略，生成校验秘钥
            const hmacKey = this.genHmacKey();
            // 按非对称加密算法策略和公钥，将对称加密秘钥和接口校验秘钥加密
            const encryptedKeys = this.genEncryptedKeys(symmetricKey, hmacKey);

            if (this.cryptConf.requestHmacEnable) {
              return negotiateCrypt(encryptedKeys).then((negotiateRes) => {
                if (negotiateRes && negotiateRes.code === 200) {
                  this.cryptKeys = {
                    symmetricKey: symmetricKey,
                    hmacKey: hmacKey,
                  };
                  this.cryptInitialized = true;
                }
              });
            } else {
              this.cryptInitialized = true;
            }
          } else {
            this.cryptInitialized = true;
          }
        })
        .finally(() => {
          setTimeout(() => {
            this.negotiating = false;
          }, 10000);
        });
    },
    rsaEncryptByKey(txt, publicKeyHex) {
      const encryptor = new JSEncrypt();
      encryptor.setPublicKey(publicKeyHex); // 设置公钥
      return encryptor.getKey().encrypt(txt);
    },
    encryptRequest(data) {
      // 请求加密
      if (this.cryptConf.requestCryptEnable) {
        let dataStr = typeof data === 'string' ? data : JSON.stringify(data);
        if ('SM4' === this.cryptConf.symmetricType) {
          // 加密，默认输出 16 进制字符串，默认使用 pkcs#7 填充（传 pkcs#5 也会走 pkcs#7 填充）
          data = sm4.encrypt(dataStr, this.cryptKeys.symmetricKey);
        } else if ('AES' === this.cryptConf.symmetricType) {
          const key = CryptoJS.enc.Hex.parse(this.cryptKeys.symmetricKey);
          data = CryptoJS.AES.encrypt(dataStr, key, {
            format: CryptoJS.format.Hex,
            mode: CryptoJS.mode.ECB,
          }).ciphertext.toString(CryptoJS.enc.Hex);
        } else {
          throw '加密失败，不支持的对称加密算法类型：' + this.cryptConf.symmetricType;
        }
      }
      return data;
    },
    hmacRequest(data, headers) {
      // 生成hmac参数
      if (this.cryptConf.requestHmacEnable) {
        // 按接口校验策略，生成校验秘钥
        let dataStr = typeof data === 'string' ? data : JSON.stringify(data);
        let timestamp = Date.now();
        let random = uuidv4().replace('-', '');
        // body:ts:from:random
        let plain = dataStr + ':' + timestamp + ':' + from + ':' + random;
        let sign;
        if ('HmacSM3' === this.cryptConf.hmacType) {
          sign = sm3(plain, { key: this.cryptKeys.hmacKey });
        } else if ('HmacSHA512' === this.cryptConf.hmacType) {
          const shaObj = new hmacSHA512(plain, CryptoJS.enc.Hex.parse(this.cryptKeys.hmacKey));
          sign = shaObj.toString(CryptoJS.enc.Hex);
        } else {
          throw 'hmac计算失败，不支持的hmac算法类型：' + this.cryptConf.symmetricType;
        }
        headers[this.cryptConf.timestampHeaderName] = timestamp;
        headers[this.cryptConf.randomHeaderName] = random;
        headers[this.cryptConf.fromHeaderName] = from;
        headers[this.cryptConf.signHeaderName] = sign;
      }
    },
    decryptResponse(data) {
      if (this.cryptConf.requestCryptEnable && typeof data === 'string') {
        if ('SM4' === this.cryptConf.symmetricType) {
          // 加密，默认输出 16 进制字符串，默认使用 pkcs#7 填充（传 pkcs#5 也会走 pkcs#7 填充）
          let decrypted = sm4.decrypt(data, this.cryptKeys.symmetricKey);
          return JSON.parse(decrypted.toString());
        } else if ('AES' === this.cryptConf.symmetricType) {
          const key = CryptoJS.enc.Hex.parse(this.cryptKeys.symmetricKey);
          let decrypted = CryptoJS.AES.decrypt(data.toString(), key, {
            format: CryptoJS.format.Hex,
            mode: CryptoJS.mode.ECB,
          });
          return JSON.parse(decrypted.toString(CryptoJS.enc.Utf8));
        } else {
          throw '加密失败，不支持的对称加密算法类型：' + this.cryptConf.symmetricType;
        }
      }
      return data;
    },
    cleanCryptKey() {
      this.cryptConf = {};
      this.cryptKeys = {};
      this.cryptInitialized = false;
      this.xSessionId = '';
    },
  },
  persist: false,
});

export default cryptStore;
